(in-package :experiment)

(defclass nback (state)
  ((n :initform 1 :initarg :n :accessor nback-n)
   (iterations :initform 10 :initarg :iterations :accessor nback-iterations)
   (letters :initform nil :accessor nback-letters
            :documentation "the letters that have appeared.")
   (letter-view :accessor nback-letter-view)
   (on-delay :initform 1.0 :initarg :letter-delay :accessor nback-on-delay)
   (off-delay :initform 0.25 :initarg :letter-delay :accessor nback-off-delay)
   (pool :initform (loop for i from (char-code #\A) to (char-code #\Z) collect (code-char i))
	 :initarg :pool
	 :accessor nback-pool)
   (state :initform :on :accessor nback-state)))

(defgeneric make-submit-view (experiment state))

(defclass kbd-nback (nback)
  ())

(defmethod make-submit-view (experiment (state nback))
  (make-instance 'gwee:button
                 :data "Match!"
                 :width 150
                 :height 50
                 :font (gwee:open-font gwee:*default-font-name* 24)
                 :click-callback (lambda (b)
                                   (declare (ignore b))
                                   (state-dispatch-event experiment state
                                                         (make-instance 'click-event :source :match)))))

(defmethod make-submit-view (experiment (state kbd-nback))
  (make-instance 'gwee:text-area
                 :data "press enter to match"
                 :height 50
                 :font (gwee:open-font gwee:*default-font-name* 18)))

(defmethod state-init (experiment (state nback))
  (log experiment 'start :iterations (nback-iterations state))
  (setf (nback-letters state)
	(list (nth (random (length (nback-pool state))) (nback-pool state)))
	(nback-letter-view state)
        (make-instance 'gwee:text-area
		       :vertical-justification :center :horizontal-justification :center
		       :data (string (car (nback-letters state)))
		       :width 50 :height 45
		       :wrap-p nil
		       :font (gwee:open-font gwee:*default-font-name* 48)))
  (log experiment 'letter :letter (first (nback-letters state)))
  (make-instance 'gwee:column-layout
                 :padding 10
                 :parent state
                 :width (gwee:view-width state)
                 :height (gwee:view-height state)
                 :subviews (list
                            (nback-letter-view state)
                            (make-submit-view experiment state)))
  (start-timer (nback-on-delay state)))

(defun nback-match-p (state)
  (and (> (length (nback-letters state)) (nback-n state))
       (char= (car (nback-letters state))
              (nth (nback-n state)
                   (nback-letters state)))))

(defmethod exp:state-list-objects append (exp (state nback))
  (list :letter (when (eq (nback-state state) :on)
                  (gwee:view-data (nback-letter-view state)))
        :buttons '(:match)))

(defmethod exp:state-list-actions append (experiment (state nback))
  (exp::default-list-actions state))

(define-action next-state ((state nback)
                           (timeout))
  (ecase (nback-state state)
    (:on
     (setf (nback-state state) :off
           (gwee:view-data (nback-letter-view state)) "")
     (start-timer (nback-off-delay state)))
    (:off
     (if (>= (length (nback-letters state)) (nback-iterations state))
         (state-onward *experiment* state)
         ;; 2/3 chance it changes
         (let ((ch (if (or (< (length (nback-letters state)) (nback-n state))
                           (nback-match-p state)
                           (< (random 9) 6))
		       (nth (random (length (nback-pool state))) (nback-pool state))
                       (nth (1- (nback-n state)) (nback-letters state)))))
           (push ch (nback-letters state))
           (log *experiment* 'letter :letter ch)
           ;; (format t "letters: ~s~%" (nback-letters state))
           (setf (nback-state state) :on
                 (gwee:view-data (nback-letter-view state)) (string ch))
           (start-timer (nback-on-delay state)))))))

(define-action match ((state nback)
                      (click :source :match))
  (log *experiment* 'match))

(define-action match ((state kbd-nback)
                      (keypress :char #\Return))
  (log *experiment* 'match))
